/**<!-------------------------------------------------------------------->
@author kdoo, bherila
@date   February 28, 2010
@brief  cs032 Intro to C Lab with Fractals
<!-------------------------------------------------------------------->**/

#include "ImageHandler.h"
#include <stdio.h>
#include <math.h>
#include <gd.h>
#include <stdlib.h>
#include <assert.h>

// Assert(a,b) is a macro that converts Assert(a,b) to assert(a && b) when the program actually runs
#define Assert(a,b) assert(a && b)

//@}-----------------------------------------------------------------
///@name Function Prototypes (private functions):/*******************
//@{----------------------------------------------------------------* 
/*******************************************************************************
 * Global Variables:
 *******************************************************************************/

/*******************************************************************************
 * Function Prototypes (private functions):
 *******************************************************************************/
FILE* openFile(const char* file_name); //opens a file (overwrites if the file already exists)
void closeFile(FILE* file);         //closes an opened file (every opened file must be closed before program exit)

/**
 * @name  saveImageDataToPngFile
 * @brief saves the data stored in the imageData array of color_t's into an output file
 * @param char* file_name
 * @param color_t* imageData
 * @param int width
 * @param int height
 **/
void saveImageDataToPngFile(const char* file_name, const color_t* imageData, int width, int height) 
{

	printf("test1\n");
    // asserts are a good way to ensure data at a provided pointer 
    // exists and input parameters are valid
    Assert(imageData, "saveImageDataToPngFile: imageData is null");
    Assert((width > 0 && height > 0), "saveImageDataToPngFile: invalid height and/or width given"); 
	printf("test2\n");
    // The following pseudocode describes how this function works.
    //   1) create a new gdImage (and assert it to make sure the pointer to it exists)
    //   2) for each pixel p in the image    
    //        i)  allocate a color for it using the RGB values from the color_t corresponding to this pixel
    //        ii) set p to be this color
    //   3) open the file we wish to save the image to
    //   4) encode the gdImage into the open file
    //   5) close the opened file
    //   6) free the resources taken up by the in-memory image
    

    // Create a new gdImage and assert that it was created successfully.
    // This creates an in-memory bitmap that we can easily manipulate.
    gdImagePtr img = gdImageCreateTrueColor(width, height);
    Assert(img, "saveImageDataToPngFile: Failed to create a png image!");
printf("test3\n");

     // Loop through all the pixels in the image:
    unsigned x, y;
int gd_color;
    for (y = 0; y < height; ++y)
    {
        for( x = 0; x < width; ++x)
        {
            // Get a gd color using the rgb data.
            gd_color = gdImageColorAllocate(img, imageData[(y * width) + x].r, imageData[(y * width) + x].g, imageData[(y * width) + x].b);
            // TODO: Set the pixel to be the proper color. You will need
            //       to use gdImageSetPixel(...).
            gdImageSetPixel(img, x, y, gd_color);
        }
}
printf("test4\n");
        FILE* toEdit = openFile(file_name);
        gdImagePng(img, toEdit);
        closeFile(toEdit);
        gdImageDestroy(img);
printf("test5\n");
    

   
    // TODO: Open the file we wish to save the image to. This should be of type FILE* and you may
    //       want to use openFile.

    // TODO: Use gdImagePng(gdImagePtr, FILE*) to save the in-memory gdImage (img) to the file you
    //       just opened.

    // TODO: Use closeFile to close the file you just opened.

    // TODO: Use gdImageDestroy to release the resources used by img

}

/*******************************************************************************
 * Function Name: saveFractalData
 * Parameters: color_t* imageData, int width, int height
 * Effects: saves the data stored in the imageData array of color_t's into an output file
 *******************************************************************************/
void generateFractalData(color_t* image_data, double width, double height)
{

    // NOTE: This algorithm is a modified version of computing a fractal.  For more information, see Wikipedia.

    // NOTE: All variables except for counters should be floats.

    // The following pseudocode describes how this function works. You may want to refer to it
    // when completing the rest of the TODOs.
    //
    // 1) for each pixel p in image_data
    //        2) compute the x and y coordinates in "fractal space" based off of the x and y pixels
    //            for this, think of a fractal as an "image" with inifinite resolution.  So in order
    //            to compute information for the image we are making, we are mapping an infinite 
    //            resolution image to a 4000x4000 pixel image, hence the need for cx and cy which 
    //            are relative coordinates of the fractal
    //
    //            The equations for cx and cy are as follows:
    //
    //            cx = (x_pixel / width * scale) - 1.5;
    //            cy = (y_pixel / height) * scale) - 2.2;
    //
    //            If scale is 3.0, the fractal will be viewed on a range from [0,3] in x
    //            and [0,3] in y.  However, as the center of the fractal is based at (0,0),
    //            we'd only be viewing the top right portion of the fractal.  Thus, we want
    //            to translate our viewing space to a range that is more reasonable, hence
    //            the -1.5 and -2.2.  With these offsets, we are now viewing the image on a 
    //            range of [-1.5,1.5] and [-2.2,.8], which allows a more complete view of the 
    //            fractal rather than just a portion of it.
    //
    //            Feel free to mess around with the scale and offsets for different effects.
    //
    //        3) reset variables x and y, and iteration to zero
    //
    //        4) while the iteration is less than max_iterations AND (x*x+y*y <= 4.0)
    //            5) x_temp = x*x-y*y+cy;
    //            6) y = 2.0*x*y+cx;
    //            7) x = x_temp;
    //
    //        5) find out how many iterations it takes to break out of step 4
    //
    //        After exiting this while loop, iteration will now be at a certain value, based on where
    //        the pixel corresponds to in "fractal space"
    //
    //        6) we can now set the specified color_t in the struct to values based off of some quadratic 
    //            (or linear) equation to yield some cool color schemes and effects.  Use whatever equations
    //            you want -- make something interesting.  It should end up being something comparable to 
    //            the fractal in the provided demo.  (If you're really uncreative feel free to look for 
    //            fractal coloring techniques on the internet)

    // 1. for each pixel p in the image_data (this is done for you...)
    // note: "unsigned" is an unsigned integer.
    unsigned y_pixel, x_pixel, iteration;
    double cx, cy, x_temp, x, y, ratio;
	for (y_pixel = 0; y_pixel < height; ++y_pixel)
	{
        	for (x_pixel = 0; x_pixel < width; ++x_pixel)
		{
			cx = ((x_pixel / width) * SCALE) - 1.5;
    	          	cy = ((y_pixel / height) * SCALE) - 2.2;
                        x = 0;
                        y = 0;
                        iteration = 0;
                        while (iteration < MAX_ITERATIONS && (x*x+y*y <= 3.0))
                        {
                            x_temp = (x*x) - (y*y) + cy;
                            y = 2.0 * x * y + cx;
                            x = x_temp;
                            iteration = iteration + 1;
                     	}
			//printf("iteration %i\n", iteration);
			//printf("max %i\n", MAX_ITERATIONS);
			ratio = iteration/ (double) MAX_ITERATIONS;
			//printf("ratio %f\n", ratio);;
                        image_data[y_pixel * (int) width + x_pixel].r = (int) ratio * 200;
                        image_data[y_pixel * (int) width + x_pixel].b = (int) ratio * 200 + 30;
                        image_data[y_pixel * (int) width + x_pixel].g = (int) ratio * 200 + 15;
		}
	}


            // TODO: 2. set cx and cy, the x-coordinate and y-coordinate in "fractal space"

            // TODO: 3. initialize x and y to zero. These are used in computing the fractal.


            // TODO: 4. compute x and y (using a temporary variable x_temp) as explained in
            //         the pseudocode.

            // TODO: 5. Assign the final color of the pixel at x_pixel, y_pixel.
            //
            // For example, image_data[y_pixel * width + x_pixel].r = 0 will set the
            // red channel of the pixel at (x_pixel, y_pixel) to zero (black).
            // The values you actually set will be some function of the ratio between iteration
            // and max_iterations.
            //
}

/*******************************************************************************
 * Function Name: openFile
 * Parameters: char* file_name
 * Effects: creates a new file if it doesn't exist, and overwrites the old file if it does exist.
 *            This function returns a FILE* pointer to the file that was opened.  As we now have an
 *            open file stream, this file must be closed before the program exits.  
 *            See closeFile(File* file).
 * Note:     This function is here to practice using pointers.
 *******************************************************************************/
FILE* openFile(const char* file_name) {
    Assert(file_name, "openFile: filename is NULL");
    return fopen(file_name, "w+");
}

/*******************************************************************************
 * Function Name: closeFile
 * Parameters: FILE* file
 * Effects: closes the file stream that corresponds to the open File* file
 * Note:     This function is here to practice using pointers.
 *******************************************************************************/
void closeFile(FILE* file) {
    Assert(file, "closeFile: file to be closed is NULL");
    fclose(file);
    file = 0;
}

/*******************************************************************************
 * Function Name: main
 * Parameters: int argc, char** argv
 * Effects: saves the data stored in the imageData array of color_t's into an output file
 *
 * Notes:  You are required to make use of the malloc function to initialize your array of colors.
 *         Make sure to use the free(void *) function to free any memory you allocate using malloc.
 *******************************************************************************/
int main(int argc, char** argv)
{
	//char file[] = "output.png";
	char *filePrinter = "output.png";
	//filePointer = &file[0];



 // TODO: Allocate an array (on the heap) of color_t structs called fractal_data.
    //       The array should be able to hold IMAGE_HEIGHT * IMAGE_WIDTH number of color_t
    //       elements. Don't forget about sizeof()!
     color_t *fractal_data = (color_t *) malloc(sizeof(color_t)*IMAGE_HEIGHT*IMAGE_WIDTH);
    
	

    // TODO: Call generateFractalData, passing in the newly allocated array and any other
    //       necessary parameters. generateFractalData will write the fractal data to the
    //       array passed in.
	generateFractalData(fractal_data, IMAGE_HEIGHT, IMAGE_WIDTH);
	

    // const char* file_name = "output.png";
    // TODO: Save the fractal data to a PNG file. Look for a helper function to do this.
	saveImageDataToPngFile(filePrinter, fractal_data, IMAGE_HEIGHT, IMAGE_WIDTH);

    // TODO: Free any memory you've allocated on the heap. Memory on the stack will be
    //       freed automatically.
	free(fractal_data);


    return 0;
}
